#pragma once

namespace mgfx
{
	enum TextureType
	{
		TEXTURE1D,
		TEXTURE2D,
		TEXTURE3D
	};

	struct TextureDesc
	{
		TextureType Type;
		uint Width, Height, Depth;
		DXGI_FORMAT Format;
		bool CreateSRV;
		DXGI_FORMAT SRVFormat;
		bool CreateUAV;
		DXGI_FORMAT UAVFormat;
		bool Dynamic;
		void * Data;
		uint DataPitch;
		uint DataSlicePitch;
	};

	typedef int TextureID;
	typedef int ShaderID;
	typedef int VertexFormatID;
	typedef int VertexBufferID;
	typedef int IndexBufferID;

	struct ShaderMacro
	{
		const char * Name;
		const char * Definition;
	};

	struct Texture;
	struct Shader;
	struct VertexBuffer;
	struct IndexBuffer;

	struct Viewport
	{
		float TopLeftX, TopLeftY;
		float MinDepth, MaxDepth;
		float Width, Height;
	};

	struct VertexFormat;

	struct VFAttribute
	{
		enum Type { POSITION = 0, TEXCOORD = 1, NORMAL = 2, TANGENT = 3, BINORMAL = 4, COLOR = 5 } Type;
		uint Stream;
		enum Format { FLOAT = 0, HALF = 1, BYTE = 2 } Format;
		uint Size;
	};

	enum PrimitiveType
	{
		PRIMITIVE_NONE = 0,
		TRIANGLE_LIST = 1
	};

	class Renderer
	{
	public:
		static Renderer * Instance;

		Renderer(ID3D11Device * device, ID3D11DeviceContext * context,
			ID3D11RenderTargetView * backbuffer, ID3D11DepthStencilView * depthStencil);
		~Renderer();

		void init();

		void setShader(ShaderID shader);
		void setShaderResource(const char * name, TextureID texId);
		void setShaderResourceArray(const char * name, TextureID * texIds, uint count);
		void setShaderUAV(const char * name, TextureID texId);
		void setShaderMatrix(const char * name, const float * data);
		void setShaderMatrixArray(const char * name, const float * data, uint count);
		void setShaderFloat(const char * name, float data);
		void setShaderFloatVector(const char * name, const float * data);
		void setShaderFloatVectorArray(const char * name, const float * data, uint count);
		void setShaderInt(const char * name, int data);
		void setShaderRawValue(const char * name, const void * data, uint sizeInBytes);
		void setVertexBuffer(VertexBufferID vb);
		void setIndexBuffer(IndexBufferID ib);
		void setVertexFormat(VertexFormatID vf);
		void setPrimitiveType(PrimitiveType type);

		void drawIndexed(uint num);
		void drawIndexed();
		void dispatch(uint threadGroupsX, uint threadGroupsY, uint threadGroupsZ);
		void drawFullScreenQuad();

		void applyConstantsWithoutDrawing();

		void setRenderTargets(uint colorNum, TextureID * colorIds, TextureID depthStencilId);
		void clear(bool clearColor, float * color, bool clearDepth = false, float depth = 0.0f, bool clearStencil = false, uint stencil = 0);

		TextureID addTexture(const char * filePath);
		TextureID addTexture2D(void * data, uint width, uint height, DXGI_FORMAT format, bool dynamic = false);
		TextureID addTexture3D(const TextureDesc & desc);
		TextureID addTexture1D(const TextureDesc & desc);
		byte * mapTexture(TextureID tex, uint * rowPitch, uint * depthPitch);
		void unmapTexture(TextureID tex);
		void resolveMSAATexture(TextureID src, TextureID dst);
		TextureID addRenderTarget(uint width, uint height, DXGI_FORMAT format, uint multisamples = 1);
		TextureID addDepthTexture(uint width, uint height, DXGI_FORMAT format, uint multisamples = 1);
		ShaderID addShader(const char * filePath, ShaderMacro * macros = NULL, uint numMacros = 0);
		ShaderID addShaderFromBinaryFile(const char * filePath);
		VertexBufferID addVertexBuffer(void * data, uint numVerts, size_t vertSize, bool dynamic = false);
		byte * mapVertexBuffer(VertexBufferID vb);
		void unmapVertexBuffer(VertexBufferID vb);
		IndexBufferID addIndexBuffer(void * data, uint numIndices, size_t indexSize, bool dynamic = false);

		VertexFormatID addVertexFormat(const VFAttribute * attribs, uint numAttrib, ShaderID shader);

		void setViewportFromTextureDim(TextureID tex);
		Viewport getViewport();
		void setViewport(const Viewport & vp);

		TextureID getDefaultFramebuffer();
		TextureID getDefaultDepthStencilBuffer();

		static const TextureID TEXTURE_NONE = -1;
		static const ShaderID SHADER_NONE = -1;
		static const VertexBufferID VB_NONE = -1;
		static const IndexBufferID IB_NONE = -1;
	private:
		
		static const uint MAX_VERTEX_STREAMS = 8;
		static const uint MAX_COLOR_RTS = 8;
		static const uint MAX_SHADER_RESOURCE_ARRAY_SIZE = 128;
		TextureID mCurrentColorRTs[MAX_COLOR_RTS];
		TextureID mCurrentDepthRt;

		void createFullScreenQuadBuffers();

		ID3D11DeviceContext * mContext;
		ID3D11Device * mDevice;

		TextureID mDefaultFramebuffer;
		TextureID mDefaultDepthStencil;

		std::vector<Texture> mTextures;

		std::vector<Shader> mShaders;
		ShaderID mCurrentShader;

		std::vector<VertexFormat> mVertexFormats;
		VertexFormatID mCurrentVertexFormat;

		PrimitiveType mCurrentPrimType;

		std::vector<VertexBuffer> mVertexBuffers;
		VertexBufferID mCurrentVertexBuffer;
		std::vector<IndexBuffer> mIndexBuffers;
		IndexBufferID mCurrentIndexBuffer;

		VertexBufferID mFullScreenQuadVB;
		IndexBufferID mFullScreenQuadIB;
	};
}